package com.moyun.common.utils;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.moyun.model.common.Constants;
import com.moyun.model.po.system.SysUserEntity;
import com.moyun.service.upload.SysAttachmentService;
import com.moyun.syssecurity.model.JwtUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @ClassName JwtTokenUtil
 * @Description josnwebtoken工具类
 * @Author yangboxuan
 * @Date 2021/5/3 17:35
 */
@Component
@RequiredArgsConstructor
public class JwtTokenUtil implements Serializable {
    private static final long serialVersionUID = 1L;

    /**
     * token有效期，单位为秒
     */
    private static final long JWT_TOKEN_VALIDITY = 5 * 60 * 60;
    @Value("${moyun-base.jwt.secret}")
    private String secret;
    private final ObjectMapper objectMapper;

    /**
     * @return java.lang.String
     * @Author yangboxuan
     * @Description 从 JWT中获取用户名
     * @Date 2021/5/3 17:45
     * @Param [token]
     */
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }

    /**
     * @return java.util.Date
     * @Author yangboxuan
     * @Description 从JWT中获取过期时间
     * @Date 2021/5/3 18:06
     * @Param [token]
     */
    public Date getexpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    /**
     * @return T
     * @Author yangboxuan
     * @Description 从JWT中获取签名
     * @Date 2021/5/3 18:09
     * @Param [token, claimsResolver]
     */
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    /**
     * @return io.jsonwebtoken.Claims
     * @Author yangboxuan
     * @Description 通过秘钥获取JWT中所有Claims信息
     * @Date 2021/5/3 17:54
     * @Param [token]
     */
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }

    /**
     * @return boolean
     * @Author yangboxuan
     * @Description 确认token是否过期
     * @Date 2021/5/3 18:14
     * @Param [token]
     */
    private boolean isTokenExpired(String token) {
        final Date expiration = getexpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * @return java.lang.String
     * @Author yangboxuan
     * @Description 用某用户生成Token
     * @Date 2021/5/3 18:34
     * @Param [userDetails]
     */
    public String generateToken(UserDetails userDetails) throws JsonProcessingException {
        SysUserEntity sysUserEntity = null;
        if (userDetails instanceof SysUserEntity) {
            sysUserEntity = (SysUserEntity) userDetails;
        }
        HashMap<String, Object> claims = new HashMap<>();
        //......此处可以根据需求在Claims中新增用户状态信息,此处将Principle以JsonString的格式加入到Claims中属于可选项
        JwtUser principal = new JwtUser()
                .setUsername(userDetails.getUsername())
//                .setPassword(userDetails.getPassword())
                .setUserId(sysUserEntity.getId())
                .setAuthorities(userDetails.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));

        claims.put(Constants.PRINCIPAL, objectMapper.writeValueAsString(principal));
        return doGenerateToken(claims, userDetails.getUsername());
    }

    /**
     * @return java.lang.String
     * @Author yangboxuan
     * @Description 生成token
     * @Date 2021/5/3 18:26
     * @Param [claims, subject]
     */
    private String doGenerateToken(Map<String, Object> claims, String subject) {
        return Jwts
                .builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + JWT_TOKEN_VALIDITY * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();

    }

    /**
     * @return boolean
     * @Author yangboxuan
     * @Description 验证token
     * @Date 2021/5/3 18:19
     * @Param [token, username]
     */
    public boolean validateToken(String token, String username) {
        return Objects.equals(getUsernameFromToken(token), username) && !isTokenExpired(token);
    }

}
